﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pixeldyne.Core.Extensions;

namespace Pixeldyne.Core.Text
{
	/// <summary>
	/// Utility extension methods.
	/// </summary>
	public static class Strings
	{
		/// <summary>
		/// Capitalises the first word in the given string, or all words in the string.
		/// </summary>
		/// <param name="s">The string to capitalise.</param>
		/// <param name="allWords">if set to <c>true</c> capitalises every word in the string.</param>
		public static string Capitalise(this string s, bool allWords)
		{
			return allWords
			       	? s.ToCharArray().Aggregate(String.Empty,
			       	                            (working, next) =>
			       	                            working.Length == 0 && next != ' '
			       	                            	? next.ToString().ToUpper()
			       	                            	: (
			       	                            	  	working.EndsWith(" ")
			       	                            	  		? working + next.ToString().ToUpper()
			       	                            	  		: working + next
			       	                            	  )
			       	  	)
			       	: char.ToUpper(s[0]) + s.Substring(1, s.Length - 1).ToLower();
		}

		/// <summary>
		/// Count the number of words in the given string
		/// </summary>
		/// <param name="text">text to count words in</param>
		/// <returns>number of words</returns>
		public static int CountWords(this string text)
		{
			if (string.IsNullOrEmpty(text))
				return -1;

			return text.Split().Count(s => s.Length > 0);
		}

		/// <summary>
		/// Determines whether a string is null or empty, by also checking a trimmed string.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <returns>
		/// 	<c>true</c> if the string is null or empty; otherwise, <c>false</c>.
		/// </returns>
		public static bool IsEmpty(this string value)
		{
			if (string.IsNullOrEmpty(value))
				return true;
			
			return value.Trim().Length == 0;
		}

		/// <summary>
		/// Reverses the specified string.
		/// </summary>
		/// <param name="value">The value.</param>
		public static string Reverse(this string value)
		{
			var chars = value.ToCharArray();
			Array.Reverse(chars);
			return new string(chars);
		}

		/// <summary>
		/// Truncates a string to the specified length
		/// </summary>
		/// <param name="sourceString">the string to truncate</param>
		/// <param name="maxLength">the max length of the string</param>
		/// <returns></returns>
		public static string Truncate(this string sourceString, int maxLength)
		{
			return sourceString.Length <= maxLength ? sourceString : sourceString.Substring(0, maxLength);
		}

		/// <summary>
		/// Converts a string to a byte array.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <param name="encoding">The encoding to use.</param>
		public static byte[] ToByteArray( this string text, Encoding encoding)
		{
			return encoding.GetBytes(text);
		}

		/// <summary>
		/// Converts a byte array to string.
		/// </summary>
		/// <param name="array">The byte array.</param>
		/// <param name="encoding">The encoding to use.</param>
		public static string ToString(this byte[] array, Encoding encoding)
		{
			return encoding.GetString(array);
		}

		/// <summary>
		/// Returns <c>true</c> if the string's length is between a range (inclusive) of values (start, end).
		/// </summary>
		/// <param name="value">The string value.</param>
		/// <param name="start">The start index.</param>
		/// <param name="end">The end index.</param>
		/// <returns></returns>
		public static bool LengthBetween(this string value, int start, int end)
		{
			return value.Length.Between(start, end, true);
		}

		/// <summary>
		/// Converts an array of strings to a single string.
		/// </summary>
		/// <param name="lines">The strings to concatenate.</param>
		public static string ArrayToString(IEnumerable<string> lines)
		{
			return string.Concat(lines);
		}

		public static char Chr(this int ansiCode)
		{
			return (char)ansiCode;
		}

		/// <summary>
		/// Returns the string representation in a hexadecimal format.
		/// </summary>
		/// <param name="bytes">The array of bytes.</param>
		public static string ToHexString(this byte[] bytes)
		{
			var sb = new StringBuilder();

			foreach (var b in bytes)
				sb.Append(b.ToString("X2"));

			return sb.ToString();
		}

		/// <summary>
		/// Encodes a string to base64.
		/// </summary>
		/// <param name="text">The string to encode.</param>
		/// <remarks>
		/// Currently using ASCII encoding.
		/// </remarks>
		static public byte[] EncodeTo64(this string text)
		{
			return Encoding.ASCII.GetBytes(text);
		}
	}
}
